package com.uuah.server.component.model.resolvers;

import java.lang.reflect.Method;

import org.apache.commons.lang.ArrayUtils;

import com.uuah.server.api.IUuahEventContext;
import com.uuah.server.component.model.InvocationResult;
import com.uuah.server.config.Constants;
import com.uuah.server.config.i18n.ServerMessages;
import com.uuah.utils.ClassUtils;

public class MethodHeaderPropertyEntryPointResolver extends
		AbstractEntryPointResolver {

	private String methodProperty = Constants.UUAH_METHOD_PROPERTY;

	public String getMethodProperty() {
		return methodProperty;
	}

	public void setMethodProperty(String methodProperty) {
		this.methodProperty = methodProperty;
	}

	public InvocationResult invoke(Object component, IUuahEventContext context)
			throws Exception {

		Object methodProp = context.getMessage().removeProperty(
				getMethodProperty());
		if (methodProp == null) {
			InvocationResult result = new InvocationResult(
					InvocationResult.STATE_INVOKED_FAILED);
			result.setErrorMessage(ServerMessages.propertyIsNotSetOnEvent(
					getMethodProperty()).toString());
			return result;
		}

		Method method;
		String methodName;
		if (methodProp instanceof Method) {
			method = (Method) methodProp;
			methodName = method.getName();
		} else {
			methodName = methodProp.toString();
			method = getMethodByName(methodName, context);
		}

		if (method != null && method.getParameterTypes().length == 0) {
			return invokeMethod(component, method, ClassUtils.NO_ARGS_TYPE);
		}

		Object[] payload = getPayloadFromMessage(context);
		if (method == null) {
			Class[] classTypes = ClassUtils.getClassTypes(payload);
			classTypes = (Class[]) ArrayUtils.addAll(
					new Class[] { IUuahEventContext.class }, classTypes);
			try {
				method = component.getClass().getMethod(methodName, classTypes);
			} catch (NoSuchMethodException e) {
				InvocationResult result = new InvocationResult(
						InvocationResult.STATE_INVOKED_FAILED);
				result.setErrorNoMatchingMethods(component, classTypes, this);
				return result;

			}
		}

		validateMethod(component, method);
		addMethodByName(method, context);

		Object[] params = ArrayUtils.addAll(new Object[] { context }, payload);
		return invokeMethod(component, method, params);

	}

	protected void validateMethod(Object component, Method method)
			throws NoSuchMethodException {

		if (method != null) {
			try {
				component.getClass().getMethod(method.getName(),
						method.getParameterTypes());
			} catch (NoSuchMethodException e) {
				throw e;
			}
		} else {
			throw new NoSuchMethodException(ServerMessages
					.methodWithParamsNotFoundOnObject("null", "unknown",
							component.getClass()).toString());

		}
	}

	public String toString() {
		final StringBuffer sb = new StringBuffer();
		sb.append("MethodHeaderPropertyEntryPointResolver");
		sb.append("{methodHeader=").append(methodProperty);
		sb.append("transformFirst=").append(isTransformFirst());
		sb.append(", acceptVoidMethods=").append(isAcceptVoidMethods());
		sb.append('}');
		return sb.toString();
	}

}
